﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Serialization;

namespace Jaiden.Updater
{
    public static class UpdatePackageHelper
    {
        public static string Serialize(UpdatePackage package)
        {
            var xml = new XmlSerializer(typeof(UpdatePackage));
            using (var ms = new MemoryStream())
            {
                xml.Serialize(ms, package);
                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }

        public static UpdatePackage Deserialize(Stream stream)
        {
            var xml = new XmlSerializer(typeof(UpdatePackage));
            return (UpdatePackage) xml.Deserialize(stream);
        }

        public static UpdatePackage Deserialize(string xml)
        {
            var serializer = new XmlSerializer(typeof(UpdatePackage));
            using (var ms = new StringReader(xml))
            {
                return (UpdatePackage) serializer.Deserialize(ms);
            }
        }

        public static UpdatePackage Build(string src, string dist)
        {
            var updatePackage = new UpdatePackage();
            if (!Directory.Exists(src))
            {
                return updatePackage;
            }

            if (!Directory.Exists(dist))
            {
                Directory.CreateDirectory(dist);
            }

            var queue = new Queue<string>();
            queue.Enqueue(src);
            while (queue.Count > 0)
            {
                var dir = queue.Dequeue();
                var children = Directory.GetDirectories(dir);
                if (children.Any())
                {
                    foreach (var child in children)
                    {
                        queue.Enqueue(child);
                    }
                }

                var files = Directory.GetFiles(dir);
                foreach (var file in files)
                {
                    var filePackage = new FilePackage
                    {
                        FilePath = file.Replace(src, string.Empty).TrimStart('\\', '/')
                    };
                    using (var fs = File.OpenRead(file))
                    {
                        filePackage.FileSize = fs.Length;
                        using (var md5 = new MD5CryptoServiceProvider())
                        {
                            var bytes = md5.ComputeHash(fs);
                            filePackage.FileHash = BitConverter.ToString(bytes).Replace("-", string.Empty);
                        }
                    }

                    filePackage.Url = filePackage.FileHash + ".zip";
                    var target = Path.Combine(dist, filePackage.Url);
                    var tmp = Path.GetTempFileName();
                    using (var tmpFileStream = File.OpenWrite(tmp))
                    {
                        using (var zip = new GZipStream(tmpFileStream, CompressionMode.Compress))
                        {
                            using (var fs = File.OpenRead(file))
                            {
                                fs.CopyTo(zip);
                            }
                        }
                    }

                    if (File.Exists(target))
                    {
                        File.Delete(target);
                    }
                    File.Move(tmp, target);
                    using (var fs = File.OpenRead(target))
                    {
                        filePackage.Size = fs.Length;
                        using (var md5 = new MD5CryptoServiceProvider())
                        {
                            var bytes = md5.ComputeHash(fs);
                            filePackage.Hash = BitConverter.ToString(bytes).Replace("-", string.Empty);
                        }
                    }
                    updatePackage.FilePackages.Add(filePackage);
                }
            }

            var xml = Path.Combine(dist, "update.xml");

            var str = Serialize(updatePackage);
            File.WriteAllText(xml, str, Encoding.UTF8);
            return updatePackage;
        }
    }
}